// PR66516 - missing diagnostic on taking the address of a builtin function
// { dg-do compile }

namespace std {
  // Define type_info type to be able to use typeid in tests without
  // having to include <typeinfo>.
  struct type_info {
    const char *name_;

    explicit type_info (const char *s): name_ (s) { }
    const char* name() const { return name_; }
  };
}

// Extern "C" since builtin functions used in tests have C linkage.
extern "C" {

typedef void (F)();
typedef __UINTPTR_TYPE__ uintptr_t;

// Utility function to test passing built-in functions as an ordinary
// argument and via the ellipsis.
static void func_arg (F*, ...);

// Utility function with which, along with the built-in function,
// to instantiate the C98 multi-parameter or C11 variadic tempates
// below.
void f () { }

}   // extern "C"


// Utility templates to test specializing templates on pointers and
// references to built-in functions.
template <F*> struct TestPointer { };
template <F&> struct TestReference { };

#if 201103 <= __cplusplus

template <F*...> struct TestPointers { };
template <F&...> struct TestReferences { };

#else

template <F* = &f, F* = &f> struct TestPointers { };
template <F& = f, F& = f> struct TestReferences { };

#endif

static F* test_taking_address_of_gcc_builtin ()
{
  enum UINTPTR_E { e = ~(uintptr_t)0 };

  F *p;
  void *q;
  uintptr_t a;

  __builtin_trap ();                           // okay
  (void)__builtin_trap;                        // okay
  __builtin_trap;                              // okay (if pointless)

  {
    typedef __typeof__ (__builtin_trap) F;     // okay
  }

#if 201103 <= __cplusplus
  {
    typedef decltype (__builtin_trap) F;       // okay

    a = noexcept (&__builtin_trap);
  }
#endif

  // Address and indirection operators.
  p = &__builtin_trap;                       // { dg-error "built-in" }
  p = *__builtin_trap;                       // { dg-error "built-in" }

  // Unary NOT.
  // GCC issues two diagnostics here for some reason, so account for both.
  a = !__builtin_trap;                   // { dg-error "built-in|unary" }

  // Casts.
  p = (F*)__builtin_trap;                    // { dg-error "built-in" }

  p = &(F&)__builtin_trap;                   // { dg-error "built-in" }

  p = &reinterpret_cast<F&>(__builtin_trap); // { dg-error "built-in" }
  p = &static_cast<F&>(__builtin_trap);      // { dg-error "built-in" }

  p = reinterpret_cast<F*>(__builtin_trap);  // { dg-error "built-in" }
  p = static_cast<F*>(__builtin_trap);       // { dg-error "built-in" }

  // Expect a diagnostic for an invalid static_cast of a function to
  // either uintptr_t or enum, rather than one for the argument being
  // a built-in function, since the former is more relevant than the latter.
  a = static_cast<uintptr_t>(__builtin_trap);       // { dg-error "7:invalid .static_cast." }
  a = static_cast<UINTPTR_E>(__builtin_trap);       // { dg-error "7:invalid .static_cast." }

  // Reinterpret cast can cast a function to uintptr_t or enum,
  // so verify that a diagnostic is issued for the use of a builtin.
  a = reinterpret_cast<uintptr_t>(__builtin_trap);  // { dg-error "built-in" }
  a = reinterpret_cast<UINTPTR_E>(__builtin_trap);  // { dg-error "built-in" }

  // Additive operator.  Ill-formed but allowed with -fpermissive.
  p = __builtin_trap + 0;            // { dg-error "built-in" }
  p = __builtin_trap - 0;            // { dg-error "built-in" }
  a = __builtin_trap - p;            // { dg-error "built-in" }
  a = p - __builtin_trap;            // { dg-error "built-in" }

  // Relational operators.  Ill-formed but allowed with -fpermissive.
  a = __builtin_trap < p;            // { dg-error "built-in|invalid template-argument-list" }
  a = p < __builtin_trap;            // { dg-error "built-in" }

  a = __builtin_trap <= p;           // { dg-error "built-in" }
  a = p <= __builtin_trap;           // { dg-error "built-in" }

  a = __builtin_trap > p;            // { dg-error "built-in" }
  a = p > __builtin_trap;            // { dg-error "built-in" }

  a = __builtin_trap > p;            // { dg-error "built-in" }
  a = p > __builtin_trap;            // { dg-error "built-in" }

  a = __builtin_trap <= p;           // { dg-error "built-in" }
  a = p <= __builtin_trap;           // { dg-error "built-in" }

  a = __builtin_trap <= p;           // { dg-error "built-in" }
  a = p <= __builtin_trap;           // { dg-error "built-in" }

  // Equality operators.
  a = __builtin_trap == p;           // { dg-error "built-in" }
  a = p == __builtin_trap;           // { dg-error "built-in" }
  a = __builtin_trap != p;           // { dg-error "built-in" }
  a = p != __builtin_trap;           // { dg-error "built-in" }

  // Logical AND and OR.
  a = __builtin_trap && p;           // { dg-error "built-in" }
  a = p && __builtin_trap;           // { dg-error "built-in" }

  a = __builtin_trap || p;           // { dg-error "built-in" }
  a = p || __builtin_trap;           // { dg-error "built-in" }

  // Conditional operator.
  a = __builtin_trap ? 1 : 0;        // { dg-error "built-in" }
  p = a ? __builtin_trap : 0;        // { dg-error "built-in" }
  p = a ? 0 : __builtin_trap;        // { dg-error "built-in" }

  // Assignment operator.
  p = __builtin_trap;                // { dg-error "built-in" }

  // Passing as an argument.
  func_arg (__builtin_trap);         // { dg-error "built-in" }
  func_arg (&__builtin_trap);        // { dg-error "built-in" }
  func_arg (*__builtin_trap);        // { dg-error "built-in" }

  // Passing through ellipsis.
  func_arg (0, __builtin_trap);      // { dg-error "built-in" }
  func_arg (0, &__builtin_trap);     // { dg-error "built-in" }
  func_arg (0, *__builtin_trap);     // { dg-error "built-in" }

  {
    // Template specialization.
    // GCC issues two diagnostics and we must account for both.
    TestPointer<__builtin_trap> tp;         // { dg-error "built-in|could not convert" }
    TestReference<__builtin_trap> tr;       // { dg-error "built-in|could not convert" }

    TestPointers<__builtin_trap> tp1;       // { dg-error "built-in|could not convert" }
    TestReferences<__builtin_trap> tr1;     // { dg-error "built-in|could not convert" }

    TestPointers<f, __builtin_trap> tp2;    // { dg-error "built-in|could not convert" }
    TestReferences<f, __builtin_trap> tr2;  // { dg-error "built-in|could not convert" }

    TestPointers<__builtin_trap, f> tp3;    // { dg-error "built-in|could not convert" }
    TestReferences<__builtin_trap, f> tr3;  // { dg-error "built-in|could not convert" }
  }

  try {
    throw __builtin_trap;                 // { dg-error "built-in" }
  }
  catch (F) { }

  return __builtin_trap;                    // { dg-error "built-in" }

  (void)a;
  (void)p;
  (void)q;
}

// Make sure operators new and delete don't trigger false positives
// (they return true from DECL_IS_BUILTIN(DECL) -- see tree.h).
void test_taking_address_of_op_new_and_delete ()
{
  typedef __SIZE_TYPE__ size_t;

  typedef void* (OpNew) (size_t);
  typedef void (OpDelete) (void*);

  OpNew &newr = operator new;
  OpNew &newra = operator new[];
  OpNew *newp = &operator new;
  newp = &operator new[];

  OpDelete &delr = operator delete;
  OpDelete &delra = operator delete[];
  OpDelete *delp = &operator delete;
  delp = &operator delete[];

  (void)newr;
  (void)newra;
  (void)newp;
  (void)delr;
  (void)delra;
  (void)delp;
}

// Helper declaration to verify that it's possible to take the address
// of a user-declared function that's also a GCC built-in.
extern int abs (int);

typedef __SIZE_TYPE__ size_t;
extern size_t strlen (const char*);

// Creating a reference to or taking the address of a built-in with
// a library "fallback" must be allowed.
void test_taking_address_of_library_builtin ()
{
  {
    typedef int F (int);

    F &r1 = __builtin_abs;
    F &r2 = *__builtin_abs;
    F *p = __builtin_abs;
    p = &__builtin_abs;
    p = *__builtin_abs;
    (void)p;
    (void)r1;
    (void)r2;
  }

  {
    typedef int F (int);

    F &r1 = abs;
    F &r2 = *abs;
    F *p = abs;
    p = &abs;
    p = *abs;
    (void)p;
    (void)r1;
    (void)r2;
  }

  {
    typedef __SIZE_TYPE__ size_t;
    typedef size_t F (const char*);
    F &r1 = __builtin_strlen;
    F &r2 = *__builtin_strlen;
    F *p = __builtin_strlen;
    p = &__builtin_strlen;
    p = *__builtin_strlen;
    (void)p;
    (void)r1;
    (void)r2;
  }

  {
    typedef size_t F (const char*);
    F &r1 = strlen;
    F &r2 = *strlen;
    F *p = strlen;
    p = &strlen;
    p = *strlen;
    (void)p;
    (void)r1;
    (void)r2;
  }
}
